"use strict";

const Command = require("@hjc-cli-dev/command");
const log = require("@hjc-cli-dev/log");
const path = require("path");
const Package = require("@hjc-cli-dev/package");
const { spinnerStart, sleep, execSync } = require("@hjc-cli-dev/utils");

const userHome = require("user-home");

const fs = require("fs");
const glob = require("glob");
const ejs = require("ejs");
const inquirer = require("inquirer"); //命令行交互包
const fsExtra = require("fs-extra");
const semver = require("semver");
const getProjectTemplate = require("./getProjectTemplate");

const TYPE_PROJECT = "project";
const TYPE_COMPONENT = "component";

const TEMPLATE_TYPE_NORMAL = "normal";
const TEMPLATE_TYPE_CUSTOM = "custom";
const WHITE_COMMAND = ["npm", "cnpm"];
class InitCommand extends Command {
    init() {
        this.projectName = this._argv[0] || "";
        this.force = !!this._cmd.force;
        log.verbose("projectName", this.projectName);
        log.verbose("force", this.force);
    }
    async exec() {
        try {
            // 准备阶段
            const projectInfo = await this.prepare();
            this.projectInfo = projectInfo;
            if (projectInfo) {
                // 下载模板
                log.verbose("projectInfo", projectInfo);
                // 1. 通过项目模板API获取项目模板信息
                // 1.1 通过egg.js搭建一套后端系统
                // 1.2 通过NPM存储项目模板
                // 1.3 将项目模板信息存储到mongodb中
                // 1.4 通过egg.js获取mongodb中的数据并通过API返回
                await this.downloadTemplate();
            }
            // 安装模板
            await this.installTemplate();
        } catch (error) {
            log.error(error.message);
            if (process.env.LOG_LEVEL === "verbose") {
                console.log(error);
            }
        }
    }
    async installTemplate() {
        if (this.templateInfo) {
            if (!this.templateInfo.type) {
                this.templateInfo.type = TEMPLATE_TYPE_NORMAL;
            }
            if (this.templateInfo.type === TEMPLATE_TYPE_NORMAL) {
                // 标准安装
                await this.installTemplateNormal();
            } else if (this.templateInfo.type === TEMPLATE_TYPE_CUSTOM) {
                // 自定义安装
                await this.installTemplateCustom();
            } else {
                throw new Error("无法识别项目模板类型！");
            }
        } else {
            throw new Error("项目模板信息不存在！");
        }
    }

    checkCommand(cmd) {
        return WHITE_COMMAND.includes(cmd) ? cmd : null;
    }
    async execCommand(command, err) {
        let isSuccess;
        if (command) {
            const cmdArr = command.split(" ");
            const cmd = this.checkCommand(cmdArr[0]);
            if (!cmd) {
                throw new Error("命令不存在！" + command);
            }
            const argv = cmdArr.slice(1);
            isSuccess = await execSync(cmd, argv, {
                stdio: "inherit",
                cwd: process.cwd(),
            });
        }
        if (isSuccess !== 0) {
            throw new Error(err);
        }
    }

    ejsRender() {
        const dir = process.cwd();
        const projectInfo = this.projectInfo;
        const ignore = this.templateInfo.ignore || [];
        const GLOB_IGNORE = ["**/node_modules/**", ...ignore];

        return new Promise((resolve, reject) => {
            glob(
                "**",
                {
                    cwd: dir, //当前的执行目录
                    ignore: GLOB_IGNORE, //忽略的文件目录
                    nodir: true, //忽略空文件夹
                },
                (err, files) => {
                    if (err) {
                        reject(error);
                    }
                    Promise.all(
                        files.map((file) => {
                            const filePath = path.join(dir, file);
                            return new Promise((resolve1, reject1) => {
                                ejs.renderFile(filePath, projectInfo, {}, (err, str) => {
                                    if (err) {
                                        reject1(err);
                                    } else {
                                        fsExtra.writeFileSync(filePath, str);
                                        resolve1(str);
                                    }
                                });
                            });
                        })
                    )
                        .then(() => {
                            resolve();
                        })
                        .catch((err) => reject(err));
                    // resolve();
                }
            );
        });
    }

    async installTemplateNormal() {
        const spinner = spinnerStart("正在安装模板中....");
        await sleep(2000);
        try {
            // 缓存目录
            const templatePath = path.resolve(this.templatePackage.cacheFilePath, "template");
            // 当前目录
            const targetPath = process.cwd();
            // 确保两个目录的存在
            fsExtra.ensureDirSync(templatePath);
            fsExtra.ensureDirSync(targetPath);
            // 拷贝缓存目录至当前目录
            fsExtra.copySync(templatePath, targetPath);
            spinner.stop(true);
        } catch (error) {
            throw new Error(error);
        } finally {
            spinner.stop(true);
            log.success("模板安装成功！");
        }
        // ejs模板渲染
        await this.ejsRender();
        const { installCommand, startCommand } = this.templateInfo;
        // 依赖安装
        await this.execCommand(installCommand, "依赖安装失败！");
        // 启动项目
        await this.execCommand(startCommand, "项目启动失败！");
    }

    async installTemplateCustom() {
        // 查询自定义模板的入口文件
        if (await this.templatePackage.exists()) {
            const rootFile = this.templatePackage.getRootFilePath();
            if (fs.existsSync(rootFile)) {
                log.notice("开始执行自定义模板");
                const templatePath = path.resolve(this.templatePackage.cacheFilePath, "template");
                const options = {
                    templateInfo: this.templateInfo,
                    projectInfo: this.projectInfo,
                    sourcePath: templatePath,
                    targetPath: process.cwd(),
                };
                const code = `require('${rootFile}')(${JSON.stringify(options)})`;
                log.verbose("code", code);
                await execSync("node", ["-e", code], { stdio: "inherit", cwd: process.cwd() });
                log.success("自定义模板安装成功");
            } else {
                throw new Error("自定义模板入口文件不存在！");
            }
        }
    }
    async downloadTemplate() {
        const { projectTemplate } = this.projectInfo;
        const templateInfo = this.template.find((ele) => ele.npmName === projectTemplate);
        const { npmName, version } = templateInfo;
        const targetPath = path.resolve(userHome, ".hjc-cli-dev", "template");
        const storeDir = path.resolve(userHome, ".hjc-cli-dev", "template", "node_modules");
        const templatePackage = new Package({
            targetPath,
            storeDir,
            packageName: npmName,
            packageVersion: version,
        });
        this.templateInfo = templateInfo;
        if (!(await templatePackage.exists())) {
            const spinner = spinnerStart("正在下载模板中....");
            await sleep(2000);
            try {
                await templatePackage.install();
            } catch (error) {
                throw error;
            } finally {
                spinner.stop(true);
                if (await templatePackage.exists()) {
                    log.success("下载模板成功！");
                    this.templatePackage = templatePackage;
                }
            }
        } else {
            const spinner = spinnerStart("正在更新模板中....");
            await sleep(2000);
            try {
                await templatePackage.update();
            } catch (error) {
                throw error;
            } finally {
                spinner.stop(true);
                if (await templatePackage.exists()) {
                    log.success("更新模板成功！");
                    this.templatePackage = templatePackage;
                }
            }
        }
    }
    async prepare() {
        // 0.判断项目模板是否存在
        const template = await getProjectTemplate();
        if (!template || !template.length) {
            throw new Error("当前模板不存在！");
        }
        this.template = template;
        const localPath = process.cwd(); //拿到当前执行命令的目录
        // 1.判断当前目录是否为空
        if (!this.ifDirEmpTy(localPath)) {
            let ifContinue = false;
            if (!this.force) {
                // 不为空询问是否继续创建
                ifContinue = (
                    await inquirer.prompt({
                        type: "confirm",
                        name: "ifContinue",
                        message: "当前文件夹不为空，是否继续创建项目？",
                        default: false,
                    })
                ).ifContinue;
                if (!ifContinue) return;
            }
            // 2.是否启动强制更新
            if (ifContinue || this.force) {
                const { confirmDelete } = await inquirer.prompt({
                    type: "confirm",
                    name: "confirmDelete",
                    message: "是否确认清空当前目录下得文件？",
                    default: false,
                });
                if (confirmDelete) {
                    // 清空当前目录
                    fsExtra.emptyDirSync(localPath);
                }
            }
        }
        return await this.getProjectInfo();
    }
    ifDirEmpTy(localPath) {
        // path.resolve('.')结果也是拿到当前执行命令的目录
        const fileList = fs.readdirSync(localPath); //拿到当前执行目录的下的文件列表
        // fileList = fileList.filter((file) => {
        //     return file.startsWith(".") && !["node_modules"].includes(file);
        // });
        return !fileList || !fileList.length;
    }

    async getProjectInfo() {
        function isVaLidName(v) {
            return /^[a-zA-Z]+([-][a-zA-Z][a-zA-Z0-9]*|[_][a-zA-Z][a-zA-Z0-9]*|[a-zA-Z0-9])*$/.test(v);
        }
        // 1.选择创建项目或者组件
        let projectInfo = {};
        let isProjectNameValid = false;

        if (isVaLidName(this.projectName)) {
            console.log(this.projectName);
            isProjectNameValid = true;
            projectInfo.projectName = this.projectName;
        }
        const { type } = await inquirer.prompt({
            type: "list",
            name: "type",
            message: "请选择初始化类型",
            default: TYPE_PROJECT,
            choices: [
                {
                    name: "项目",
                    value: TYPE_PROJECT,
                },
                {
                    name: "组件",
                    value: TYPE_COMPONENT,
                },
            ],
        });
        log.verbose(type);
        const title = type === TYPE_PROJECT ? "项目" : "组件";
        this.template = this.template.filter((ele) => ele.tag.includes(type));
        const inquirerArr = [
            {
                type: "input",
                name: "projectVersion",
                message: `请输入${title}版本`,
                default: "",
                validate: function (v) {
                    const done = this.async();
                    setTimeout(function () {
                        if (!semver.valid(v)) {
                            done("请输入合法的版本号！");
                            return;
                        }
                        done(null, true);
                    }, 0);
                },
                filter: (v) => {
                    return semver.valid(v) || v;
                },
            },
            {
                type: "list",
                name: "projectTemplate",
                message: `请选择${title}版本`,
                choices: this.createTemplateChoices(),
            },
        ];
        if (!isProjectNameValid) {
            inquirerArr.unshift({
                type: "input",
                name: "projectName",
                message: `请输入${title}名称`,
                default: "",
                validate: function (v) {
                    // 首字符必须为因为字符
                    // 尾字符必须为英文或者数字
                    // 字符仅允许_-
                    const done = this.async();
                    setTimeout(function () {
                        if (!isVaLidName(v)) {
                            done(`请输入合法得${title}名称！`);
                            return;
                        }
                        done(null, true);
                    }, 0);
                },
                filter: (v) => {
                    return v;
                },
            });
        }
        if (type === TYPE_PROJECT) {
            // 2.获取项目的基本信息
            const project = await inquirer.prompt(inquirerArr);
            projectInfo = {
                ...projectInfo,
                type,
                ...project,
            };
        } else if (type === TYPE_COMPONENT) {
            inquirerArr.push({
                type: "input",
                name: "description",
                message: "请输入组件的描述信息",
                default: "",
                validate: function (v) {
                    const done = this.async();
                    setTimeout(function () {
                        if (!v) {
                            done("组件描述信息不能为空！");
                            return;
                        }
                        done(null, true);
                    }, 0);
                },
            });
            const component = await inquirer.prompt(inquirerArr);
            projectInfo = {
                ...projectInfo,
                type,
                ...component,
            };
        }
        if (projectInfo.projectName) {
            projectInfo.className = require("kebab-case")(projectInfo.projectName).replace(/^-/, "");
        }
        return projectInfo;
        // return 项目基本信息
    }
    createTemplateChoices() {
        return this.template.map(({ name, npmName }) => {
            return { name, value: npmName };
        });
    }
}
function init(argv) {
    return new InitCommand(argv);
}

module.exports = init;
